/**
 * 小程序图片分析工具
 * 
 * 功能：
 * 1. 分析项目中的图片使用情况
 * 2. 检测未使用的图片资源
 * 3. 检测重复或相似图片
 * 4. 检测过大的图片文件
 * 5. 生成优化建议报告
 */

const fs = require('fs');
const path = require('path');
const crypto = require('crypto');

// 配置
const config = {
  // 图片目录
  imageDirs: [
    'images',
    'src/device_types/seawater_lamp/images',
    'src/device_types/freshwater_lamp/images',
    'assets/icons'
  ],
  // 代码目录
  codeDirs: [
    'pages',
    'components',
    'src'
  ],
  // 图片扩展名
  imageExts: ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp'],
  // 代码文件扩展名
  codeExts: ['.js', '.wxml', '.wxss', '.json', '.wxs'],
  // 大图片阈值（字节）
  largeImageThreshold: 200 * 1024, // 200KB
  // 排除目录
  excludeDirs: ['node_modules', '.git', 'miniprogram_npm', 'webp-tools']
};

class ImageAnalyzer {
  constructor() {
    this.imageFiles = [];
    this.codeFiles = [];
    this.imageUsage = new Map();
    this.unusedImages = [];
    this.largeImages = [];
    this.similarImages = [];
    this.stats = {
      totalImages: 0,
      totalImageSize: 0,
      unusedImages: 0,
      unusedImagesSize: 0,
      largeImages: 0,
      largeImagesSize: 0,
      similarImageGroups: 0
    };
  }

  // 递归查找文件
  _findFilesRecursive(dir, extensions, fileList) {
    if (!fs.existsSync(dir)) return;
    
    const files = fs.readdirSync(dir);
    
    for (const file of files) {
      const filePath = path.join(dir, file);
      const stat = fs.statSync(filePath);
      
      if (stat.isDirectory() && !config.excludeDirs.some(excludeDir => filePath.includes(excludeDir))) {
        this._findFilesRecursive(filePath, extensions, fileList);
      } else if (stat.isFile() && extensions.includes(path.extname(filePath).toLowerCase())) {
        fileList.push(filePath);
      }
    }
  }

  // 查找所有图片文件
  findImageFiles() {
    console.log('🔍 扫描图片文件...');
    
    for (const dir of config.imageDirs) {
      if (!fs.existsSync(dir)) {
        console.warn(`⚠️ 图片目录不存在: ${dir}`);
        continue;
      }
      
      this._findFilesRecursive(dir, config.imageExts, this.imageFiles);
    }
    
    this.stats.totalImages = this.imageFiles.length;
    console.log(`找到 ${this.imageFiles.length} 个图片文件`);
    
    // 计算总图片大小
    for (const image of this.imageFiles) {
      const size = fs.statSync(image).size;
      this.stats.totalImageSize += size;
      
      // 初始化图片使用情况
      this.imageUsage.set(image, {
        path: image,
        size: size,
        sizeFormatted: this._formatSize(size),
        references: [],
        used: false
      });
    }
    
    console.log(`总图片大小: ${this._formatSize(this.stats.totalImageSize)}`);
  }

  // 查找所有代码文件
  findCodeFiles() {
    console.log('🔍 扫描代码文件...');
    
    for (const dir of config.codeDirs) {
      if (!fs.existsSync(dir)) {
        console.warn(`⚠️ 代码目录不存在: ${dir}`);
        continue;
      }
      
      this._findFilesRecursive(dir, config.codeExts, this.codeFiles);
    }
    
    console.log(`找到 ${this.codeFiles.length} 个代码文件`);
  }

  // 分析图片使用情况
  analyzeImageUsage() {
    console.log('📊 分析图片使用情况...');
    
    for (const codeFile of this.codeFiles) {
      try {
        const content = fs.readFileSync(codeFile, 'utf8');
        
        // 对每个图片检查在代码中的引用
        for (const [imagePath, imageInfo] of this.imageUsage) {
          // 生成可能的引用路径变体
          const pathVariations = this._generatePathVariations(imagePath);
          
          // 检查每个路径变体
          for (const pathVar of pathVariations) {
            if (content.includes(pathVar)) {
              imageInfo.used = true;
              imageInfo.references.push(codeFile);
              break;
            }
          }
        }
      } catch (error) {
        console.error(`❌ 读取文件失败 ${codeFile}: ${error.message}`);
      }
    }
    
    // 找出未使用的图片
    for (const [imagePath, imageInfo] of this.imageUsage) {
      if (!imageInfo.used) {
        this.unusedImages.push(imageInfo);
        this.stats.unusedImages++;
        this.stats.unusedImagesSize += imageInfo.size;
      }
    }
    
    console.log(`未使用图片: ${this.unusedImages.length} 个，总大小: ${this._formatSize(this.stats.unusedImagesSize)}`);
  }

  // 检测大图片
  detectLargeImages() {
    console.log('🔍 检测大图片...');
    
    for (const [imagePath, imageInfo] of this.imageUsage) {
      if (imageInfo.size > config.largeImageThreshold) {
        this.largeImages.push(imageInfo);
        this.stats.largeImages++;
        this.stats.largeImagesSize += imageInfo.size;
      }
    }
    
    // 按大小排序
    this.largeImages.sort((a, b) => b.size - a.size);
    
    console.log(`大图片: ${this.largeImages.length} 个，总大小: ${this._formatSize(this.stats.largeImagesSize)}`);
  }

  // 计算图片哈希值（用于相似性比较）
  _calculateImageHash(imagePath) {
    try {
      const buffer = fs.readFileSync(imagePath);
      return crypto.createHash('md5').update(buffer).digest('hex');
    } catch (error) {
      console.error(`❌ 计算图片哈希值失败 ${imagePath}: ${error.message}`);
      return null;
    }
  }

  // 检测相似图片
  detectSimilarImages() {
    console.log('🔍 检测相似图片...');
    
    // 按扩展名分组
    const imagesByExt = {};
    
    for (const imagePath of this.imageFiles) {
      const ext = path.extname(imagePath).toLowerCase();
      if (!imagesByExt[ext]) {
        imagesByExt[ext] = [];
      }
      imagesByExt[ext].push(imagePath);
    }
    
    // 对每个扩展名组内的图片进行比较
    for (const ext in imagesByExt) {
      const images = imagesByExt[ext];
      
      // 如果该扩展名下只有1个或0个图片，跳过
      if (images.length <= 1) continue;
      
      // 计算每个图片的哈希值
      const imageHashes = new Map();
      for (const imagePath of images) {
        const hash = this._calculateImageHash(imagePath);
        if (hash) {
          imageHashes.set(imagePath, hash);
        }
      }
      
      // 按哈希值分组找出完全相同的图片
      const hashGroups = {};
      for (const [imagePath, hash] of imageHashes) {
        if (!hashGroups[hash]) {
          hashGroups[hash] = [];
        }
        hashGroups[hash].push(imagePath);
      }
      
      // 提取有多张图片的组（即相同的图片）
      for (const hash in hashGroups) {
        if (hashGroups[hash].length > 1) {
          // 获取这些图片的详细信息
          const similarGroup = {
            hash: hash,
            images: hashGroups[hash].map(imagePath => {
              const size = fs.statSync(imagePath).size;
              return {
                path: imagePath,
                size: size,
                sizeFormatted: this._formatSize(size),
                used: this.imageUsage.get(imagePath).used
              };
            })
          };
          
          this.similarImages.push(similarGroup);
          this.stats.similarImageGroups++;
        }
      }
    }
    
    console.log(`发现 ${this.similarImages.length} 组相似图片`);
  }

  // 生成分析报告
  generateReport() {
    console.log('📝 生成分析报告...');
    
    const reportContent = `# 小程序图片资源分析报告

## 总体情况

- 分析时间: ${new Date().toLocaleString()}
- 图片总数: ${this.stats.totalImages} 个
- 图片总大小: ${this._formatSize(this.stats.totalImageSize)}
- 未使用图片: ${this.stats.unusedImages} 个 (${this._formatSize(this.stats.unusedImagesSize)})
- 大图片(>${this._formatSize(config.largeImageThreshold)}): ${this.stats.largeImages} 个 (${this._formatSize(this.stats.largeImagesSize)})
- 相似图片组: ${this.stats.similarImageGroups} 组

## 优化潜力

通过优化，预计可节省: ${this._formatSize(this.stats.unusedImagesSize + this.stats.largeImagesSize / 2)}

## 未使用图片

以下图片在代码中没有引用，可考虑删除:

${this.unusedImages.map(img => `- ${img.path} (${img.sizeFormatted})`).join('\n')}

## 大图片

以下图片尺寸过大，建议优化:

${this.largeImages.slice(0, 20).map(img => `- ${img.path} (${img.sizeFormatted})`).join('\n')}
${this.largeImages.length > 20 ? `... 以及 ${this.largeImages.length - 20} 个其他大图片` : ''}

## 相似图片

以下图片组可能是重复或相似的:

${this.similarImages.map((group, index) => {
  return `### 相似组 ${index + 1}\n\n` + 
         group.images.map(img => `- ${img.path} (${img.sizeFormatted})`).join('\n');
}).join('\n\n')}

## 优化建议

1. **删除未使用图片**：删除上述未引用的图片，可节省 ${this._formatSize(this.stats.unusedImagesSize)}

2. **优化大图片**：
   - 将大图片转换为WebP格式
   - 使用 \`utils/image_optimizer.js\` 工具进行批量处理
   - 为大图片创建延迟加载机制 (使用 \`components/lazy_image\` 组件)

3. **合并相似图片**：
   - 检查相似图片组，删除重复图片
   - 将多个图标合并为雪碧图 (sprite)

4. **使用自适应图片**：
   - 使用 \`utils/adaptive_image_handler.js\` 工具创建不同尺寸版本
   - 根据设备屏幕大小加载合适尺寸的图片

5. **清理未使用的资源**：
   - 定期运行图片分析工具
   - 建立资源管理规范

## 执行命令

可以使用以下命令对图片进行优化:

\`\`\`bash
# 优化所有大图片
node utils/image_optimizer.js

# 创建自适应图片
node utils/adaptive_image_handler.js

# 生成延迟加载图片组件
node utils/lazy_image_component.js
\`\`\`
`;
    
    // 保存报告
    const reportDir = 'docs/reports';
    if (!fs.existsSync(reportDir)) {
      fs.mkdirSync(reportDir, { recursive: true });
    }
    
    const reportFile = path.join(reportDir, 'image_analysis_report.md');
    fs.writeFileSync(reportFile, reportContent);
    
    console.log(`✅ 分析报告已生成: ${reportFile}`);
    
    return reportFile;
  }

  // 运行完整分析
  async run() {
    console.log('🚀 开始图片资源分析...\n');
    
    // 1. 查找所有图片文件
    this.findImageFiles();
    
    // 2. 查找所有代码文件
    this.findCodeFiles();
    
    // 3. 分析图片使用情况
    this.analyzeImageUsage();
    
    // 4. 检测大图片
    this.detectLargeImages();
    
    // 5. 检测相似图片
    this.detectSimilarImages();
    
    // 6. 生成报告
    const reportFile = this.generateReport();
    
    // 7. 输出总结
    console.log('\n✨ 图片分析完成!');
    console.log(`- 图片总数: ${this.stats.totalImages} 个 (${this._formatSize(this.stats.totalImageSize)})`);
    console.log(`- 未使用图片: ${this.stats.unusedImages} 个 (${this._formatSize(this.stats.unusedImagesSize)})`);
    console.log(`- 大图片: ${this.stats.largeImages} 个 (${this._formatSize(this.stats.largeImagesSize)})`);
    console.log(`- 相似图片组: ${this.stats.similarImageGroups} 组`);
    console.log(`\n分析报告: ${reportFile}`);
    
    return `
图片分析完成！可优化空间: ${this._formatSize(this.stats.unusedImagesSize + this.stats.largeImagesSize / 2)}

详细报告: ${reportFile}

建议的后续步骤:
1. 删除未使用的 ${this.stats.unusedImages} 个图片
2. 使用image_optimizer.js优化 ${this.stats.largeImages} 个大图片
3. 检查 ${this.stats.similarImageGroups} 组相似/重复图片
`;
  }

  // 辅助方法 - 格式化文件大小
  _formatSize(bytes) {
    if (bytes < 1024) {
      return `${bytes} B`;
    } else if (bytes < 1024 * 1024) {
      return `${(bytes / 1024).toFixed(2)} KB`;
    } else if (bytes < 1024 * 1024 * 1024) {
      return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
    } else {
      return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
    }
  }

  // 辅助方法 - 生成可能的路径变体
  _generatePathVariations(filepath) {
    const variations = [];
    
    // 1. 原始路径
    variations.push(filepath);
    
    // 2. 规范化路径分隔符
    const normalized = filepath.replace(/\\/g, '/');
    variations.push(normalized);
    
    // 3. 相对路径变体
    if (normalized.startsWith('./')) {
      variations.push(normalized.substring(2));
    } else {
      variations.push('./' + normalized);
    }
    
    // 4. 绝对路径变体（以/开头）
    if (!normalized.startsWith('/')) {
      variations.push('/' + normalized);
    }
    
    // 5. 文件名
    const fileName = path.basename(filepath);
    variations.push(fileName);
    
    // 6. 不同扩展名变体（处理可能的格式转换，如.png -> .webp）
    const baseName = path.basename(filepath, path.extname(filepath));
    const dirName = path.dirname(normalized);
    
    config.imageExts.forEach(ext => {
      variations.push(`${dirName}/${baseName}${ext}`);
      variations.push(`/${dirName}/${baseName}${ext}`);
      variations.push(`${baseName}${ext}`);
    });
    
    return [...new Set(variations)]; // 去重
  }
}

// 主函数
async function main() {
  const analyzer = new ImageAnalyzer();
  await analyzer.run();
}

// 执行
main().catch(err => {
  console.error(`❌ 执行出错: ${err.message}`);
  process.exit(1);
}); 